﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TeamOPSchoolCup.Domain.DAL;
using TeamOPSchoolCup.Domain.POCO.Ranking;
using TeamOPSchoolCup.Domain.POCO.Members;
using TeamOPSchoolCup.Domain.POCO;
using TeamOPSchoolCup.Domain.DTO;
using TeamOPSchoolCup.Domain.POCO.Meetings;
using System.Data;

namespace TeamOPSchoolCup.Domain.Services
{
  public class RankingService
  {
    //Get Ranking (individual)
    //Update the ranking
    //Adjust filters
    //  I don't get the badlengte filter, we don't save that ANYWHERE? Or is that different events?

    public enum Officiality { OFFICIAL, UNOFFICIAL, ALL }

    public static bool ImportPoints(PointsImport pi)
    {
      bool success;
      using (EFDbContext db = new EFDbContext())
      {
        success = ImportPoints(db, pi);
      }
      return success;
    }

    public static bool ImportPoints(EFDbContext db, PointsImport pi)
    {
      foreach (PointsImport.ImportLine line in pi.Lines)
      {
        if (line.BoysMeasure!=0) db.Points.Add(new Point() { CompetitionYear = pi.CompetitionYear, EventId = pi.Event.EventId, Gender = Gender.MALE, Measure = line.BoysMeasure, PointValue = line.PointValue });
        if (line.GirlsMeasure!=0) db.Points.Add(new Point() { CompetitionYear = pi.CompetitionYear, EventId = pi.Event.EventId, Gender = Gender.FEMALE, Measure = line.GirlsMeasure, PointValue = line.PointValue });
      }
      return (db.SaveChanges() > 0);
    }

    public static bool AddPoint(Point point)
    {
      bool success;
      using (EFDbContext db = new EFDbContext())
      {
        success = AddPoint(db, point);
      }
      return success;
    }

    public static bool AddPoint(EFDbContext db, Point point)
    {
      db.Points.Add(point);
      return (db.SaveChanges() > 0);
    }

    // THE NEW CODE:

    public static bool CalculateBestStudentResult(EFDbContext db, Result result)
    {
      List<Result> results = ResultService.GetResults(db, result.TeamMembership.Student, result.Event, result.Meeting.Date.Year, (result.Meeting.IsOfficial && result.Event.IsOfficial ? RankingService.Officiality.OFFICIAL : RankingService.Officiality.UNOFFICIAL));
      Result bestResult = ResultService.GetResultWithBestScoreWithinEvent(db, results);
      BestStudentResult bsr = null;
      if ((bsr = RankingService.GetBestStudentResult(db, result.TeamMembership.Student, result.Event, result.Meeting.Date.Year, (result.Meeting.IsOfficial && result.Event.IsOfficial ? RankingService.Officiality.OFFICIAL : RankingService.Officiality.UNOFFICIAL))) != null)
      {
        if (bsr.ResultId == bestResult.ResultId) return true;
        bsr.Result = bestResult;
        db.Entry(bsr).State = EntityState.Modified;
        return (db.SaveChanges() > 0);
      }
      bsr = new BestStudentResult { Student = result.TeamMembership.Student, Event = result.Event, CompetitionYear = result.Meeting.Date.Year, IsOfficial = (result.Meeting.IsOfficial && result.Event.IsOfficial), Result = result };
      db.BestStudentResults.Add(bsr);
      return (db.SaveChanges() > 0);
    }

    public static BestStudentResult GetBestStudentResult(EFDbContext db, Student student, Event pEvent, int year, RankingService.Officiality officiality)
    {
      bool bOfficiality = (officiality == RankingService.Officiality.OFFICIAL);
      BestStudentResult bsr = db.BestStudentResults.Where(x => x.StudentId == student.StudentId && x.Event.EventId == pEvent.EventId && x.CompetitionYear == year && x.IsOfficial == bOfficiality).FirstOrDefault();
      return bsr;
    }

    public static bool AddTeamResult(EFDbContext db, Meeting meeting, Team team)
    {
      List<Result> results = ResultService.GetResults(db, meeting, team);
      int totalPoints = 0;
      IEnumerable<IGrouping<int, Result>> groupinglist = results.GroupBy(x => x.EventId);
      foreach (IGrouping<int, Result> group in groupinglist)
      {
        Result bestResult = ResultService.GetResultWithBestScoreWithinEvent(db, group.ToList());
        int? specificPoints = RankingService.CalculatePoints(db, bestResult);
        if (specificPoints.HasValue) totalPoints += specificPoints.Value;
      }
      TeamResult tr = new TeamResult { Meeting = meeting, Team = team, PointValue = totalPoints };
      db.TeamResults.Add(tr);
      bool b = (db.SaveChanges() > 0);
      RankingService.SetBestTeamResult(db, tr);
      return b;
    }

    public static int? CalculatePoints(EFDbContext db, Result result)
    {
      List<Point> points = db.Points.Where(x => x.EventId == result.EventId && x.CompetitionYear == result.Meeting.Date.Year && x.Gender_Value == ((short)result.TeamMembership.Student.Member.Gender)).OrderByDescending(x => x.PointValue).ToList();
      if (points == null) return null;
      Point p;
      if (result.Event.Directionality == Event.EDirectionality.ASC)
      {
        p = points.Where(x => x.Measure <= result.ResultScore).FirstOrDefault();
      }
      else
      {
        p = points.Where(x => x.Measure >= result.ResultScore).FirstOrDefault();
      }
      if (p == null) return null;
      return p.PointValue;
    }

    public static bool SetBestTeamResult(EFDbContext db, TeamResult tr)
    {
      List<TeamResult> trs = db.TeamResults.Where(x => x.TeamId == tr.TeamId && x.Meeting.Date.Year == tr.Meeting.Date.Year).ToList();
      TeamResult bestTr = trs.OrderByDescending(x => x.PointValue).First();
      BestTeamResult btr = db.BestTeamResults.Where(x => x.TeamId == tr.TeamId && x.CompetitionYear == tr.Meeting.Date.Year).FirstOrDefault();
      if (btr != null)
      {
        if (btr.TeamId == bestTr.TeamId && btr.MeetingId == bestTr.MeetingId) return true;
        db.BestTeamResults.Remove(btr);
        db.SaveChanges();
      }
      btr = new BestTeamResult { TeamId = bestTr.TeamId, MeetingId = bestTr.MeetingId, CompetitionYear = bestTr.Meeting.Date.Year };
      db.BestTeamResults.Add(btr);
      return (db.SaveChanges() > 0);
    }

    public static List<BestStudentResult> GetStudentRanking(int? year = null, RankingService.Officiality officiality = RankingService.Officiality.ALL, Gender? gender = null, Event pEvent = null, Region region = null)
    {
      List<BestStudentResult> bsrs = null;
      using (EFDbContext db = new EFDbContext())
      {
        bsrs = GetStudentRanking(db, year, officiality, gender, pEvent, region);
      }
      return bsrs;
    }

    //TODO
    //  * Badlengte filter
    //  * Leeftijdscategoriefilter
    //  * Geografische filter
    //  * Order!
    public static List<BestStudentResult> GetStudentRanking(EFDbContext db, int? year = null, RankingService.Officiality officiality = RankingService.Officiality.ALL, Gender? gender = null, Event pEvent = null, Region region = null)
    {
      List<BestStudentResult> bsrs = db.BestStudentResults.ToList();
      if (year.HasValue) bsrs = bsrs.Where(x => x.CompetitionYear == year).ToList();
      if (officiality != RankingService.Officiality.ALL)
      {
        bool bOfficial = (officiality == RankingService.Officiality.OFFICIAL);
        bsrs = bsrs.Where(x => x.IsOfficial == bOfficial).ToList();
      }
      if (gender.HasValue) bsrs = bsrs.Where(x => x.Student.Member.Gender == gender).ToList();
      if (pEvent != null) bsrs = bsrs.Where(x => x.EventId == pEvent.EventId).ToList();
      return bsrs;
    }

    public static List<BestTeamResult> GetTeamRanking(int? year = null)
    {
      List<BestTeamResult> btrs = null;
      using (EFDbContext db = new EFDbContext())
      {
        btrs = GetTeamRanking(db, year);
      }
      return btrs;
    }

    public static List<BestTeamResult> GetTeamRanking(EFDbContext db, int? year = null)
    {
      List<BestTeamResult> btrs = db.BestTeamResults.ToList();
      if (year.HasValue) btrs = btrs.Where(x => x.CompetitionYear == year).ToList();
      btrs = btrs.OrderByDescending(x => x.TeamResult.PointValue).ToList();
      return btrs;
    }
  }
}
